package com.example.common.utils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public final class ShortCodeUtil {

    private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
    private static final long BASE = 62L;
    private static final int CODE_LEN = 6;
    private static final long SPACE = pow(BASE, CODE_LEN); // 62^6

    // 固定盐值（写死在工具类里）
    private static final String FIXED_SALT = "Internal-Static-Salt-2025";

    private ShortCodeUtil() { }

    /**
     * 生成 6 位短码。
     * @param originalUrl 原始URL（只用其长度）
     * @param userId 用户ID
     * @return 6位 [0-9A-Za-z] 字符串
     */
    public static String generate(String originalUrl, String userId) {
        Objects.requireNonNull(originalUrl, "originalUrl");
        Objects.requireNonNull(userId, "userId");

        String msg = originalUrl.length() + ":" + userId;
        byte[] digest = hmacSha256(FIXED_SALT, msg);
        long first64 = toUnsignedLong(digest, 0);
        long reduced = Math.floorMod(first64, SPACE);
        return encodeBase62Fixed(reduced, CODE_LEN);
    }

    /**
     * 支持冲突重试用（可加 counter）
     */
    public static String generateWithCounter(String originalUrl, String userId, int counter) {
        Objects.requireNonNull(originalUrl, "originalUrl");
        Objects.requireNonNull(userId, "userId");

        String msg = originalUrl.length() + ":" + userId + "#" + counter;
        byte[] digest = hmacSha256(FIXED_SALT, msg);
        long first64 = toUnsignedLong(digest, 0);
        long reduced = Math.floorMod(first64, SPACE);
        return encodeBase62Fixed(reduced, CODE_LEN);
    }

    // --- 内部方法 ---

    private static byte[] hmacSha256(String key, String message) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            return mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            throw new RuntimeException("HMAC-SHA256 failed", e);
        }
    }

    private static long toUnsignedLong(byte[] bytes, int offset) {
        ByteBuffer bb = ByteBuffer.wrap(bytes, offset, 8).order(ByteOrder.BIG_ENDIAN);
        long asLong = bb.getLong();
        return asLong ^ Long.MIN_VALUE;
    }

    private static String encodeBase62Fixed(long value, int len) {
        char[] out = new char[len];
        for (int i = len - 1; i >= 0; i--) {
            int rem = (int) (value % BASE);
            out[i] = BASE62[rem];
            value /= BASE;
        }
        return new String(out);
    }

    private static long pow(long a, int b) {
        long r = 1;
        for (int i = 0; i < b; i++) r *= a;
        return r;
    }

    // 演示
    public static void main(String[] args) {
        String url = "https://example.com/a/b/c?x=1&y=2";
        String userId = "1145141919810";

        String code = ShortCodeUtil.generate(url, userId);
        System.out.println("code = " + code);
    }
}
